home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1996-09-17 | 21.8 KB | 738 lines | [ TEXT/MPS ]
//======================================================================================== // // File: FWDrCmd.cpp // Release Version: $ ODF 2 $ // // Copyright: (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved. // //======================================================================================== #include "FWFrameW.hpp" #ifndef FWDRCMD_H #include "FWDrCmd.h" #endif #ifndef FWINTER_H #include "FWInter.h" #endif // ----- Framework Includes ----- #ifndef FWPART_H #include "FWPart.h" #endif #ifndef FWFRAME_H #include "FWFrame.h" #endif #ifndef FWDRGDRP_H #include "FWDrgDrp.h" #endif #ifndef FWSELECT_H #include "FWSelect.h" #endif #ifndef FWEVENT_H #include "FWEvent.h" #endif #ifndef FWUTIL_H #include "FWUtil.h" #endif #ifndef FWPRESEN_H #include "FWPresen.h" #endif #ifndef FWLNKMGR_H #include "FWLnkMgr.h" #endif #ifndef FWCONTXT_H #include "FWContxt.h" #endif #ifndef FWSESION_H #include "FWSesion.h" #endif // ----- OS Layer ----- #ifndef FWMENUS_K #include "FWMenus.k" #endif #ifndef FWODGEOM_H #include "FWODGeom.h" #endif #ifndef FWREGION_H #include "FWRegion.h" #endif #ifndef FWBARRAY_H #include "FWBArray.h" #endif #ifndef SLODFSTR_K #include "SLODFStr.k" #endif #ifndef SLODFSTR_H #include "SLODFStr.h" #endif // ----- OpenDoc Includes ----- #ifndef SOM_Module_OpenDoc_Commands_defined #include <CmdDefs.xh> #endif #ifndef SOM_ODClipboard_xh #include <Clipbd.xh> #endif #ifndef SOM_Module_OpenDoc_StdProps_defined #include <StdProps.xh> #endif #ifndef SOM_Module_OpenDoc_StdTypes_defined #include <StdTypes.xh> #endif #ifndef SOM_ODStorageUnit_xh #include <StorageU.xh> #endif #ifndef SOM_ODFacet_xh #include <Facet.xh> #endif #ifndef SOM_ODDragAndDrop_xh #include <DragDrp.xh> #endif #ifndef SOM_ODDragItemIterator_xh #include <DgItmIt.xh> #endif #ifndef _TRANSFORM_ #include <Trnsform.xh> #endif #ifndef SOM_ODLinkSpec_xh #include <LinkSpec.xh> #endif // ----- Macintosh Includes ----- #if defined(FW_BUILD_MAC) && !defined(__DRAG__) #include <Drag.h> #endif //======================================================================================== // Runtime Info //======================================================================================== #ifdef FW_BUILD_MAC #pragma segment odfcommands #endif FW_DEFINE_AUTO(FW_CDragCommand) FW_DEFINE_AUTO(FW_CDropCommand) //======================================================================================== // FW_CDragCommand class //======================================================================================== //---------------------------------------------------------------------------------------- // FW_CDragCommand constructor //---------------------------------------------------------------------------------------- FW_CDragCommand::FW_CDragCommand(Environment* ev, FW_CFrame* frame, FW_Boolean canUndo) : FW_CCommand(ev, FW_kDragCommand, frame, canUndo), fSelection(NULL), fBeganTransaction(false), fDragMoveToAnotherPart(false), fDestPartIsDifferent(false) { fDragResult = kODDropFail; if (GetCanUndo(ev)) { FW_CString undoString; FW_CString redoString; ::FW_PrivLoadUndoStrings(ev, FW_kUndoDropMsg, undoString, redoString); SetMenuStrings(ev, undoString, redoString); SetActionType(ev, kODEndAction); } fSelection = frame->GetPresentation(ev)->GetSelection(ev); FW_ASSERT(fSelection != NULL); // Must have a selection object for drag FW_END_CONSTRUCTOR } //---------------------------------------------------------------------------------------- // FW_CDragCommand destructor //---------------------------------------------------------------------------------------- FW_CDragCommand::~FW_CDragCommand() { FW_START_DESTRUCTOR } //---------------------------------------------------------------------------------------- // FW_CDragCommand::DoIt //---------------------------------------------------------------------------------------- void FW_CDragCommand::DoIt(Environment* ev) // Override { if (fDragMoveToAnotherPart) // it's a move to another part { if (GetCanUndo(ev)) { SaveUndoState(ev); } fSelection->ClearSelection(ev); } // ----- Check for failure, and if so clean up if (fDragResult == kODDropFail) // Drag&Drop operation failed somewhere along the line { SetCausesChange(ev, FALSE); // Don't call part->Changed() SetCanUndo(ev, FALSE); // cancel the Undo! if (fBeganTransaction) // we've already added a Begin action { AbortTransaction(ev); // clear unfinished transactions from Undo stack fBeganTransaction = FALSE; } } else if (fBeganTransaction && !GetCanUndo(ev)) // fCanUndo was turned off after BeginAction was added { ODPart* part; ODActionData actionData; ODActionType actionType; ODName actionLabel; actionLabel.text._buffer = kODNULL; actionData._buffer = kODNULL; if (fDestPartIsDifferent && fDragResult == kODDropCopy) { // Check previous action if (FW_CSession::GetUndo(ev)->PeekUndoHistory(ev, &part, &actionData, &actionType, &actionLabel)) { if ((actionType == kODBeginAction) && (actionData._maximum == 0)) { // Mary says this block ensures that there won't be an undo action when // a drag-copy to the desktop or other non-opendoc destination is performed // I havnen't seen this assertion fail or seen an inapropriate undo menu // item, so that case may be being handled in some other way. FW_Boolean aborting_empty_transaction_in_FW_CDragCommand_DoIt = false; FW_ASSERT(aborting_empty_transaction_in_FW_CDragCommand_DoIt); // Clear empty Begin transaction from Undo stack AbortTransaction(ev); fBeganTransaction = false; } } } if (fBeganTransaction) { // Add an EndAction to finish the transaction, since Execute won't AddAction(ev, kODEndAction, NULL, 0, GetUndoString(ev), GetRedoString(ev)); } } if (!this->IsDragMoveToAnotherPart(ev)) this->SetCausesChange(ev, FALSE); // ----- Finally if undoable save the redo state if (GetCanUndo(ev)) SaveRedoState(ev); } //---------------------------------------------------------------------------------------- // FW_CDragCommand::CommitDone //---------------------------------------------------------------------------------------- void FW_CDragCommand::CommitDone(Environment* ev) { if (GetCanUndo(ev) && HasAddedAction(ev) && fDragMoveToAnotherPart) FreeUndoState(ev); } //---------------------------------------------------------------------------------------- // FW_CDragCommand::BeginDrag //---------------------------------------------------------------------------------------- void FW_CDragCommand::BeginDrag(Environment* ev, const FW_CMouseEvent& theMouseEvent) { ODPart* destPart = NULL; ODDragAndDrop* drag; ODStorageUnit* dragSU; // ----- Ask the command to save the dragged content and the undo state----- ODFacet* facet = theMouseEvent.GetFacet(ev); FW_CFrame* scopeFrame = GetFrame(ev); FW_ASSERT(facet->GetFrame(ev) == scopeFrame->GetODFrame(ev)); ODRgnHandle dragRegion = NULL; // ----- In the following block we focus on the window ----- { FW_CWindowContext wc(ev, facet); FW_CRect selectionBounds; // selectionBounds is in content coordinate // ----- Scope for aqDragShape and aqWindowContentTransform ----- { FW_CAcquiredODShape aqDragShape(CreateDragShape(ev, facet)); selectionBounds = FW_GetShapeBoundingBox(ev, aqDragShape); FW_ContentToWindow(ev, facet, aqDragShape); dragRegion = ::FW_CopyRegion(::FW_GetShapeRegion(ev, aqDragShape)); } #ifdef FW_BUILD_MAC // ----- dragRegion must be in screen coordinate on the Mac Point localToGlobal = {FW_QDGlobals.thePort->portRect.top, FW_QDGlobals.thePort->portRect.left}; ::LocalToGlobal(&localToGlobal); FW_CPoint offset(localToGlobal); ::FW_OffsetRegion(dragRegion, offset.x, offset.y); #endif // ----- Fill Drag and Drop SU with Props n' Vals drag = FW_CSession::GetDragAndDrop(ev); drag->Clear(ev); dragSU = drag->GetContentStorageUnit(ev); // ----- write out the contents of the drag GetPart(ev)->GetDataInterchange(ev)->ExternalizeData(ev, fSelection->GetSelectedContent(ev), scopeFrame, dragSU, FW_kDragAndDropStorage, kODCloneCut); // ----- save offset between mousedown pt and topLeft pt of selection // (must convert to Content coordinates first) FW_CPoint mouseDownOffset = theMouseEvent.GetMousePosition(ev, FW_CMouseEvent::kFrame); // FW_CFrame* frame = FW_CFrame::ODtoFWFrame(ev, facet->GetFrame(ev)); // frame->GetContentView(ev)->FrameToViewContent(ev, mouseDownOffset); scopeFrame->GetContentView(ev)->FrameToViewContent(ev, mouseDownOffset); mouseDownOffset -= selectionBounds.TopLeft(); dragSU->AddProperty(ev, kODPropMouseDownOffset)->AddValue(ev, kODPoint); FW_CByteArray byteArray(&mouseDownOffset, sizeof(FW_CPoint)); dragSU->SetValue(ev, byteArray); if (GetCanUndo(ev)) // post an empty Begin action { AddAction(ev, kODBeginAction, NULL, 0, GetUndoString(ev), GetRedoString(ev)); fBeganTransaction = TRUE; } // ----- Write out Link Spec if possible ----- if (!GetPart(ev)->IsReadOnly(ev)) { FW_CLinkManager* linkMgr = GetPart(ev)->GetLinkManager(ev); if (linkMgr && fSelection->IsSelectionLinkable(ev)) { ODLinkSpec* linkSpec = NULL; FW_VOLATILE(linkSpec); ODUpdateID updateID = FW_CSession::UniqueUpdateID(ev); // need data to put in the link spec FW_CByteArray data(&updateID, sizeof(updateID)); FW_TRY { linkSpec = GetPart(ev)->GetDraft(ev)->CreateLinkSpec(ev, GetPart(ev)->GetODPart(ev), data); dragSU->AddProperty(ev, kODPropLinkSpec); linkSpec->WriteLinkSpec(ev, dragSU); } FW_CATCH_BEGIN FW_CATCH_EVERYTHING() { // exception occurred - just continue without the link spec delete linkSpec; // deleting NULL is fine linkSpec = NULL; // FW_THROW_SAME(); } FW_CATCH_END if (linkSpec) { // Create a link FW_CLinkSource* link = linkMgr->NewLinkSource(ev, updateID, GetPresentation(ev)); linkMgr->PrivSetPendingDropLink(ev, link, scopeFrame); delete linkSpec; } } } FW_CMouseEvent dragEvent(ev, theMouseEvent); ODEventData* eventData = dragEvent.GetPlatformEvent(); FW_CByteArray dragRegionBArray(&dragRegion, sizeof(dragRegion)); FW_CByteArray dragEventBArray(&eventData, sizeof(ODEventData*)); scopeFrame->PrivStartStopDragging(ev, FW_kStartDrag); FW_TRY { fDragResult = drag->StartDrag(ev, scopeFrame->GetODFrame(ev), kODDragImageRegionHandle, dragRegionBArray, &destPart, dragEventBArray); } FW_CATCH_BEGIN FW_CATCH_EVERYTHING() { ::FW_DisposeRegion(dragRegion); scopeFrame->PrivStartStopDragging(ev, FW_kDragAborted); if (destPart) destPart->Release(ev); FW_THROW_SAME(); } FW_CATCH_END GetPart(ev)->GetDataInterchange(ev)->PrivDeletePromises(ev, FW_kDragAndDropStorage); // [HLX] ??? delete any drag and drop promises because we wan't need them // ----- Dispose the Drag region ----- ::FW_DisposeRegion(dragRegion); dragRegion = NULL; // ----- Determine if it was a dragmove to another part fDestPartIsDifferent = (destPart != GetPart(ev)->GetODPart(ev)); fDragMoveToAnotherPart = (fDragResult == kODDropMove) && fDestPartIsDifferent; // ----- Done Dragging ----- scopeFrame->PrivStartStopDragging(ev, fDragMoveToAnotherPart ? FW_kDragMove : FW_kDragCopy); // ----- Release the destination part ----- if (destPart) destPart->Release(ev); // ----- If was not a DragMove to another part if (!fDragMoveToAnotherPart) // can only Undo drag-move to another part SetCanUndo(ev, FALSE); } } //---------------------------------------------------------------------------------------- // FW_CDragCommand::CreateDragShape //---------------------------------------------------------------------------------------- ODShape* FW_CDragCommand::CreateDragShape(Environment* ev, ODFacet* facet) { FW_ASSERT(facet); return FW_CopyAndRelease(ev, fSelection->AcquireSelectionOutline(ev, facet, GetFrame(ev))); } //---------------------------------------------------------------------------------------- // FW_CDragCommand::AbortTransaction //---------------------------------------------------------------------------------------- void FW_CDragCommand::AbortTransaction(Environment* ev) { FW_CSession::GetUndo(ev)->AbortCurrentTransaction(ev); } //======================================================================================== // FW_CDropCommand //======================================================================================== //---------------------------------------------------------------------------------------- // FW_CDropCommand constructor //---------------------------------------------------------------------------------------- FW_CDropCommand::FW_CDropCommand(Environment* ev, FW_CFrame* frame, ODDragItemIterator* dropInfo, ODFacet* facet, const FW_CPoint& dropPoint, FW_Boolean canUndo) : FW_CCommand(ev, FW_kDropCommand, frame, canUndo), FW_MPasteAsHandler(ev, frame), fDropItemIterator(dropInfo), fFacet(facet), fDropPoint(dropPoint), fSelection(NULL) { fDroppedInSameFrame = FALSE; fDropResult = kODDropFail; fSelection = frame->GetPresentation(ev)->GetSelection(ev); if (GetCanUndo(ev)) { FW_CString undoString; FW_CString redoString; ::FW_PrivLoadUndoStrings(ev, FW_kUndoDropMsg, undoString, redoString); SetMenuStrings(ev, undoString, redoString); } FW_ASSERT(fSelection != NULL); // Must have a selection object for drag FW_END_CONSTRUCTOR } //---------------------------------------------------------------------------------------- // FW_CDropCommand destructor //---------------------------------------------------------------------------------------- FW_CDropCommand::~FW_CDropCommand() { FW_START_DESTRUCTOR } //---------------------------------------------------------------------------------------- // FW_CDropCommand::DoIt //---------------------------------------------------------------------------------------- FW_DECLARE_THROW_POINT(FW_CDropCommand_DoIt_BeforeHandleDrop) FW_DECLARE_THROW_POINT(FW_CDropCommand_DoIt_AfterHandleDrop) void FW_CDropCommand::DoIt(Environment* ev) { FW_MDroppableFrame* droppable = GetFrame(ev)->GetDroppable(ev); FW_ASSERT(droppable != NULL); if (!this->IsOKtoEdit(ev) || !droppable->GetCanAcceptDrop(ev)) fDropResult = kODDropFail; else { if (GetCanUndo(ev)) SaveUndoState(ev); FW_TRY { FW_CHECK_THROW_POINT (FW_CDropCommand_DoIt_BeforeHandleDrop); fDropResult = PrivHandleDrop(ev); } FW_CATCH_BEGIN FW_CATCH_EVERYTHING () { if (GetCanUndo(ev)) FreeUndoState (ev); FW_THROW_SAME (); } FW_CATCH_END } FW_CHECK_THROW_POINT (FW_CDropCommand_DoIt_AfterHandleDrop); if (fDropResult == kODDropFail) // Drop failed--can't Undo { if (GetCanUndo(ev)) FreeUndoState(ev); // Discard any data saved for Undo SetCanUndo(ev, FALSE); // Don't call AddActionToHistory SetCausesChange(ev, FALSE); // Don't call GetPart(ev)->Changed() } // Now that we've successfully dropped, "protect" it by supressing any subsequent // failures. We *don't* rethrow now because undo is less important. if (GetCanUndo(ev)) { FW_TRY { SaveRedoState(ev); } FW_CATCH_BEGIN FW_CATCH_EVERYTHING () { // Having already called PrivHandleDrop and performed the major work of this // command we *really* want to avoid passing exceptions up. It would be nice // if we could put up an alert saying that Redo won't be supported, though. } FW_CATCH_END } } //---------------------------------------------------------------------------------------- // FW_CDropCommand::CommitUndone //---------------------------------------------------------------------------------------- void FW_CDropCommand::CommitUndone(Environment* ev) { if (GetCanUndo(ev) && !IsDragMoveInSameFrame(ev)) FreeRedoState(ev); } //---------------------------------------------------------------------------------------- // FW_CDropCommand::DoDrop //---------------------------------------------------------------------------------------- FW_Boolean FW_CDropCommand::DoDrop( Environment* ev, ODStorageUnit* dropSU, const FW_CPoint& mouseDownOffset, const FW_CPoint& dropPoint, FW_Boolean isDropMove, short itemNumber) { FW_UNUSED(mouseDownOffset); FW_UNUSED(dropPoint); FW_UNUSED(itemNumber); return GetPart(ev)->GetDataInterchange(ev)->InternalizeData(ev, GetPresentation(ev)->GetSelection(ev)->GetSelectedContent(ev), GetFrame(ev), dropSU, FW_kDragAndDropStorage, (isDropMove ? kODCloneDropMove : kODCloneDropCopy), GetEmbedInfoPtr(ev)) != FW_kInternalizeFailed; } //---------------------------------------------------------------------------------------- // FW_CDropCommand::DoDroppedInSameFrame //---------------------------------------------------------------------------------------- FW_Boolean FW_CDropCommand::DoDroppedInSameFrame(Environment* ev, ODStorageUnit* dropSU, const FW_CPoint& mouseDownOffset, const FW_CPoint& dropPoint) { FW_UNUSED(ev); FW_UNUSED(dropSU); FW_UNUSED(mouseDownOffset); FW_UNUSED(dropPoint); return FALSE; } //---------------------------------------------------------------------------------------- // FW_CDropCommand::PrivReadMouseDownOffset //---------------------------------------------------------------------------------------- FW_CPoint FW_CDropCommand::PrivReadMouseDownOffset(Environment* ev, ODStorageUnit *dropSU) const { FW_CPoint mouseDownOffset; if (dropSU->Exists(ev, kODPropMouseDownOffset, kODPoint, 0)) { dropSU->Focus(ev, kODPropMouseDownOffset, kODPosUndefined, kODPoint, 0, kODPosFirstSib); FW_CByteArray byteArray; dropSU->GetValue(ev, sizeof(FW_CPoint), byteArray); byteArray.CopyBuffer(&mouseDownOffset, sizeof(FW_CPoint)); } return mouseDownOffset; } //---------------------------------------------------------------------------------------- // FW_CDropCommand::PrivHandleDrop //---------------------------------------------------------------------------------------- FW_DECLARE_THROW_POINT (FW_CDropCommand_PrivHandleDrop_AfterFirstDrop); ODDropResult FW_CDropCommand::PrivHandleDrop(Environment* ev) { ODStorageUnit* dropSU; ODDropResult dropResult = kODDropFail; // assume the worst FW_Boolean acceptedDrop = true; FW_Boolean handledDrop = false; ODDragAndDrop* drag = FW_CSession::GetDragAndDrop(ev); unsigned long attributes = drag->GetDragAttributes(ev); FW_Boolean isDropMove = (attributes & kODDropIsMove); FW_Boolean isDropInSameFrame = (attributes & kODDropIsInSourceFrame); FW_Boolean isDropInSamePart = (attributes & kODDropIsInSourcePart); if (attributes & kODDropIsPasteAs) // user wants Paste As dialog { dropSU = fDropItemIterator->First(ev); acceptedDrop = HandleDropPasteAsDialog(ev, dropSU, handledDrop); if (handledDrop && acceptedDrop) { // ----- Get the Mouse Offset ----- FW_CPoint mouseDownOffset = PrivReadMouseDownOffset(ev, dropSU); if (GetNewLink(ev) == NULL) SetCommandID(ev, kODCommandPasteAs); else SetCommandID(ev, FW_kPasteWithLinkCommand); DoDroppedPasteAs(ev, mouseDownOffset, fDropPoint); if (GetNewLink(ev) != NULL) // If the drop created a link, return kODDropCopy regardless of the drop attributes. return kODDropCopy; } // If the user chose to Merge with Contents without a Link, handledDrop is FALSE // and the dropped data will be handled below. } if (!handledDrop) { /* Regarding exception handling... What happens if we are dropping several items and the first goes OK but a later one causes an exception to be thrown? Should we try to undo the previous drops or just exit (not having fully cleaned up)? We are not going to clean up, on the theory that you probably want dropped objects to stick around. If whoever called us assumes that we are going to either completely succeed or completely fail (i.e. not exit with a partial state change) then there may be a problem. This situation comes up every time we have a loop with a state change in it. */ short itemNumber = 0; // ----- Iterate thru dropped items for (dropSU = fDropItemIterator->First(ev); dropSU && acceptedDrop; dropSU = fDropItemIterator->Next(ev)) { itemNumber++; // ----- Get the Mouse Offset ----- FW_CPoint mouseDownOffset = PrivReadMouseDownOffset(ev, dropSU); // ----- Just make sure we are dealing with the same frame ----- FW_ASSERT(GetFrame(ev) == FW_CFrame::ODtoFWFrame(ev, fFacet->GetFrame(ev))); // ----- Check if we were dragging in the same presentation. In this case // ----- even if we were dragging accross frames I still want to just call // ----- DoDroppedInSameFrame. FW_Boolean samePresentation = (isDropInSamePart) && GetPresentation(ev)->PrivIsDragPending(); // ----- Always try to activate the frame (and the window) ----- GetFrame(ev)->ActivateWindow(ev, fFacet, TRUE); fDroppedInSameFrame = ((samePresentation || isDropInSameFrame) && isDropMove); if (fDroppedInSameFrame) acceptedDrop = DoDroppedInSameFrame(ev, dropSU, mouseDownOffset, fDropPoint); else acceptedDrop = DoDrop(ev, dropSU, mouseDownOffset, fDropPoint, isDropMove, itemNumber); FW_CHECK_THROW_POINT (FW_CDropCommand_PrivHandleDrop_AfterFirstDrop); } } // ----- Return drop result if (acceptedDrop) dropResult = (isDropMove ? kODDropMove : kODDropCopy); return dropResult; } //---------------------------------------------------------------------------------------- // FW_CDropCommand::DoDroppedPasteAs //---------------------------------------------------------------------------------------- void FW_CDropCommand::DoDroppedPasteAs(Environment* ev, const FW_CPoint& mouseDownOffset, const FW_CPoint& dropPoint) { FW_UNUSED(ev); FW_UNUSED(mouseDownOffset); FW_UNUSED(dropPoint); // Override to perform post-Drop positioning on the items that were dropped }